/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufba.lasid.ds.replication;

import br.ufba.lasid.base.api.IResource;
import br.ufba.lasid.comm.api.ICommunicator;
import br.ufba.lasid.comm.api.IHost;
import br.ufba.lasid.ds.replication.api.IReplicatedResourceManager;
import br.ufba.lasid.ds.replication.api.IReplicatedResourceManager.ModuleTypes;
import br.ufba.lasid.ds.replication.api.IReplicatedResourcePeriodicActionModule;
import br.ufba.lasid.utils.DateUtil;
import br.ufba.lasid.utils.ReplicaRole;
import br.ufba.lasid.utils.StateXen;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 *
 * @author aliriosa
 */
public class ReplicatedResourcePeriodicActionModule implements IReplicatedResourcePeriodicActionModule {

    private IReplicatedResourceManager replicator;
    private long period = 100L;
    private static final Log logger = LogFactory.getLog(ReplicatedResourceManager.class);
    private ICommunicator communicator;
    private ReplicaRole style;

    public ReplicatedResourcePeriodicActionModule(IReplicatedResourceManager replicator) {
        this.replicator = replicator;
    }

    public ReplicatedResourcePeriodicActionModule() {
    }

    @Override
    public String getName() throws Exception {
        return this.getModuleType().name();
    }

    @Override
    public ModuleTypes getModuleType() throws Exception {
        return ModuleTypes.PeriodicAction;
    }

    @Override
    public void setReplicatedResource(IReplicatedResourceManager replicator) throws Exception {
        this.replicator = replicator;
    }

    @Override
    public synchronized void setPeriod(long period) {
        this.period = period;
    }

    @Override
    public synchronized long getPeriod() {
        return this.period;
    }

    @Override
    public void startup() throws Exception {
        new Thread(this).start();
    }

    @Override
    public void run() {
        try {
            communicator = replicator.getCommunicator();
            style = replicator.getResource().getRole();

            while (true) {
                try {
                    Thread.sleep(getPeriod());
                    perform();
                } catch (Exception ex) {
                    logger.error("A runtime error has been detected.", ex);
                }
            }
        } catch (Exception ex) {
            logger.error("couldn't start", ex);
        }
    }

    @Override
    public void perform() throws Exception {
        /**
         * If the replicated resource uses a PRIMARY monitoring style, then
         * it'll periodically make a checkPoint on localhost.
         */
        if (ReplicaRole.Primary.equals(replicator.getResource().getRole())) {
            logger.debug("Making checkpoint ... ");

            executeCheckPointGarbageCollection();

            if (StateXen.IDLE.equals(replicator.getVm().getStateByName())) {
                releaseStatusCheckPoint();
                String exe = "xm save -c " + replicator.getVm().getName() + " " + replicator.getVmPath() + "-NOK-" + DateUtil.getCheckPointTime(new Date()) + ".chk";
                logger.info(exe);

                Runtime.getRuntime().exec(exe);
                logger.debug(" ... Making Check Point in background!\n");
            } else {
                logger.debug(" ... Check Point was not executed because the VM id is not IDLE or the VM name is wrong!\n");
            }

        } else if (ReplicaRole.Secondary.equals(replicator.getResource().getRole())) {
            // logger.debug("Making restore ... ");
            // String exe = "xm restore " + replicator.getLastCheckPoint();
        } else {
            logger.error("Error on performing Periodic Actions!");
            throw new RuntimeException();
        }
    }

    @Override
    public void releaseStatusCheckPoint() throws IOException {

        String c = "ls " + replicator.getVmPath() + "-NOK-* | tail -1";

        Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", c});

        BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));

        String pOut = in.readLine();

        logger.info(pOut);

        if (StringUtils.isNotEmpty(pOut)) {
            c = "mv " + pOut + " " + pOut.replace("NOK", "OK");
            logger.info(c);
            Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", c});
        }
    }

    @Override
    public void executeCheckPointGarbageCollection() throws IOException, InterruptedException {
        logger.debug("Executing CheckPoint Garbage Collection ... ");
        String exe = "ls " + replicator.getResource().getPath() + replicator.getVm().getName() + "-OK-* |head -n-3 |xargs rm";
        logger.info(exe);

        Runtime.getRuntime().exec(new String[]{"/bin/bash", "-c", exe});
        // Thread.sleep(5000L);
        logger.debug("Success Garbage Collection!");
    }

    public void scheduleTimeout(IHost host) throws Exception {
        String hostID = host.getID();
    }

    public void cancelTimeout(IHost host) throws Exception {
        String hostID = host.getID();
    }

    class Sender extends Thread {

        IHost host;
        String message;

        public Sender() {
        }

        public Sender(IHost host, String message) {
            this.host = host;
            this.message = message;
        }

        @Override
        public void run() {
            String hostID = host.getID();
            try {
                communicator.send(hostID, replicator.getName(), message);
            } catch (ConnectException ce) {
                host.setState(IResource.States.Down);
                //ce.printStackTrace();
            } catch (NoRouteToHostException nre) {
                host.setState(IResource.States.Down);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
